using Cysharp.Threading.Tasks;
using DG.Tweening;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using UnityEngine;
using UnityEngine.Assertions;

namespace IQIGame.Onigao.GamePlay
{
    public abstract class EntityMoveComponent : TReusableEvtComponent
    {
        /// <summary>
        /// joyStick目标power（用于动画过度）
        /// </summary>
        private float _animationTargetSpeed;

        /// <summary>
        /// 中间数据，插值使用
        /// </summary>
        private Vector3 targetPositon;

        /// <summary>
        /// 移动方向（不需要移动时为Vector3.zero，注意该方向并未归一化）
        /// </summary>
        private Vector3 autoMoveUnnormalizedDir;

        /// <summary>
        /// 移动类型（保持垂直，不改变朝向，跟随移动方向改变朝向）
        /// </summary>
        private EntityMoveDirType dirType;

        /// <summary>
        /// 实体的实际速度比率
        /// </summary>
        private float serverSyncSpeedRatio;

        /// <summary>
        /// 动画速度
        /// </summary>
        private float _animationSpeed;

        private Transform _keepLookAtTrans;
        private float _animVelocity;
        private int? _rotationYAfterMove;

        public abstract TransformAgent transform { get; }
        protected abstract EntityAnimationComponent animComponent { get; }

        protected abstract TNavigationController navigationCtrl { get; }

        /// <summary>
        /// 主角移动碰撞保底使用
        /// </summary>
        protected CharacterController characterController { get; set; }

        /// <summary>
        /// 是否是主角
        /// </summary>
        public bool isPlayerAvatarEntity { get; set; }

        public Quaternion quaterion { private set; get; }
        public bool rotatonNeedRestore { set; get; }

        public override void OnCreate(TEntity entity)
        {
            if (entity is TViewEntity viewEntity)
            {
                this._entity = viewEntity;
            }
            else
            {
                LogGame.LogError("该组件不支持非TViewEntity类型的entity。");
            }
        }

        public override void OnUpdate(float deltaTime)
        {
            if (transform == null)
            {
                return;
            }

            if (_keepLookAtTrans != null)
            {
                transform.LookAt(_keepLookAtTrans);
            }

            AutoMoveUpdate(deltaTime);
            UpdateSpeedDamp(deltaTime);
        }

        protected virtual void OnHide()
        {
            if (this.characterController != null)
            {
                this.characterController.enabled = false;
            }
        }

        /// <summary>
        /// 统一的设置坐标方法
        /// 所有transform.position = position都必须通过它来实现
        /// </summary>
        /// <param name="position"></param>
        /// <param name="isMoving"></param>
        protected virtual void SetPosition(Vector3 position, bool isMoving = false)
        {
            transform.position = position;
        }

        public override void OnComponentEvent<T>(TComponentEvent eventType, T arg)
        {
            switch (eventType)
            {
                case TComponentEvent.OnEntityObjectLoaded:
                    EntityLoadState loadState = default;
                    if (arg is EntityLoadState els)
                    {
                        loadState = els;
                    }

                    if (loadState == EntityLoadState.None || loadState == EntityLoadState.Error)
                    {
                        return;
                    }

                    Assert.IsNotNull(this.transform, "[EntityMoveComponent|OnCreate] Transform is null");
                    if (this.isPlayerAvatarEntity)
                    {
                        AddCharacterController();
                        return;
                    }

                    break;

                case TComponentEvent.OnEntityObjectHided:
                    OnHide();
                    break;
            }
        }

        protected void AddCharacterController()
        {
            GameObject go = transform.gameObject;
            // 主角特殊，使用CharacterController
            this.characterController = go.GetComponent<CharacterController>();
            if (this.characterController == null)
            {
                this.characterController = go.AddComponent<CharacterController>();
            }

            this.characterController.height = ViewEntityConst.CharacterHeight - characterController.skinWidth;
            var center = this.characterController.center;
            center.y = this.characterController.height * 0.5f + characterController.skinWidth;
            this.characterController.center = center;
            this.characterController.radius = ViewEntityConst.CharacterRadius;
        }

        #region 直接控制移动，而不是相应服务器同步

        /// <summary>
        /// 直接传送位置坐标
        /// </summary>
        /// <param name="position"></param>
        public virtual void TransferLocation(Vector3 position)
        {
            SetPosition(position);
            StopPositionUpdate();
        }

        /// <summary>
        /// 往一个角度移动一小步，并且使用CharacterController修正碰撞
        /// 一般适用于摇杆移动
        /// </summary>
        /// <param name="angle"></param>
        /// <param name="power"></param>
        public void MoveStep(float angle, float power)
        {
            if (!isPlayerAvatarEntity)
            {
                LogGame.LogError("组件属性不是玩家可操作的，不能使用该方法!");
                return;
            }

            SetAnimTargetSpeed(power);

            var forward = ManagerCenter.UI.MainCamera.transform.forward;
            forward = Vector3.ProjectOnPlane(forward, Vector3.up);
            Quaternion quaternion = Quaternion.LookRotation(forward) * Quaternion.Euler(0, -angle + 90, 0);
            //这里quaternion * Vector3.forward不要用transform.forward替代，尽量不要依赖transform，
            //因为主城里使用了特殊的transformAgent，只为position实现了实时数据同步，直接依赖transform获取数据可能是错误的
            //当然要解决这个问题也可以通过增加缓存代码和接口api来实现，但能直接通过计算获得的数据不依赖transform更安全
            var offset = quaternion * Vector3.forward * _entity.defaultMeterSpeed * power * Time.deltaTime;

            transform.rotation = quaternion;

            MoveStep(transform.position + offset, power, false);
        }

        /// <summary>
        /// 朝指定位置移动一小步，注意目标位置不能太远，否则表现可能会很跳跃
        /// 该方法主要为摇杆移动和自动导航准备，内部逻辑和传送其实差不多，只是处理了一部分移动的逻辑，比如朝向，动画
        /// </summary>
        /// <param name="targetPosNearBy"></param>
        /// <param name="power"></param>
        public void MoveStep(Vector3 targetPosNearBy, float power, bool adjustRotation)
        {
            //手动移动，所以会打断自动移动
            StopPositionUpdate();

            SetAnimTargetSpeed(power);

            if (targetPosNearBy == _entity.unityPosOfData)
            {
                return;
            }

            transform.DOKill();

            if (adjustRotation)
            {
                var p = targetPosNearBy;
                p.y = transform.position.y;
                transform.LookAt(p);
            }

            Vector3 realEndPos;

            if (isPlayerAvatarEntity)
            {
                //玩家实体移动
                if (!MoveStepInner(targetPosNearBy, out realEndPos))
                {
                    return;
                }
            }
            else
            {
                //非玩家实体移动
                //非玩家实体MoveStep都是由自动寻路驱动，Recast的寻路接口会校验网格，不用再次校验，所以checkNavMesh传false
                if (!MoveStepInnerNoCharacterCtrl(targetPosNearBy, false, out realEndPos))
                {
                    return;
                }
            }

            _entity.UpdatePosToData(realEndPos);
        }

        /// <summary>
        /// 移动实体
        /// 该实体不依赖UnityEngine.CharacterController
        /// </summary>
        /// <param name="tryEndPos"></param>
        /// <param name="checkNavMesh"></param>
        /// <param name="realEndPos"></param>
        /// <returns></returns>
        protected virtual bool MoveStepInnerNoCharacterCtrl(Vector3 tryEndPos, bool checkNavMesh,
            out Vector3 realEndPos)
        {
            var curPos = _entity.unityPosOfData;

            if (checkNavMesh)
            {
                bool result = navigationCtrl.TryMove(curPos, tryEndPos, out realEndPos);
                if (!result)
                {
                    return false;
                }
            }
            else
            {
                realEndPos = tryEndPos;
            }

            //射线检测来修正脚底高度，SimpleMove边缘的时候会跳下去，SimpleMove只能传速度，无法控制落点。需要控制主角在导航网格内的落点
            if (GameScenePlayUtil.TryGroundRaycast(tryEndPos, 1, out var hit))
            {
                realEndPos.y = hit.point.y;
            }

            transform.position = realEndPos;

            return true;
        }

        protected virtual bool MoveStepInner(Vector3 pos, out Vector3 realPos)
        {
            return NavigationMoveStep(pos, out realPos);
        }

        /// <summary>
        /// 设置动画目标速度，然后逐渐过渡到这个速度
        /// </summary>
        /// <param name="speedRatio"></param>
        protected void SetAnimTargetSpeed(float speedRatio)
        {
            _animationTargetSpeed = speedRatio;
        }

        /// <summary>
        /// 平滑修改动画速度
        /// </summary>
        /// <param name="deltaTime"></param>
        protected void UpdateSpeedDamp(float deltaTime)
        {
            if (_animationTargetSpeed >= 0)
            {
                //启动和停止速度都需要逐步变化
                var animSpeed = _animationSpeed;
                animSpeed = Mathf.SmoothDamp(animSpeed, _animationTargetSpeed, ref _animVelocity,
                    animSpeed < _animationTargetSpeed ? 0.15f : 0.25f);
                animSpeed = Mathf.Abs(animSpeed - _animationTargetSpeed) < 0.01f ? _animationTargetSpeed : animSpeed;
                SetAnimationSpeed(animSpeed);
                if (animSpeed == _animationTargetSpeed)
                {
                    _animationTargetSpeed = -1;
                }
            }
        }

        public void StopMove(bool stopAnimationNow = false)
        {
            StopPositionUpdate();
            if (stopAnimationNow) //立即停止移动动画，硬切回闲置动画
            {
                _animationTargetSpeed = -1;
                SetAnimationSpeed(0);
            }
            else //缓慢切回闲置动画
            {
                //速度过快播放急刹动画
                if (_animationSpeed >= ViewEntityConst.PlayerAvatarBrakeSpeed)
                {
                    animComponent?.PlayBrake();
                }

                _animationTargetSpeed = 0;
            }
        }

        /// <summary>
        /// 改变动画速度
        /// </summary>
        /// <param name="speedRatio"></param>
        private void SetAnimationSpeed(float speedRatio)
        {
            _animationSpeed = speedRatio;
            animComponent?.SetMoveSpeed(_entity.defaultMeterSpeed * speedRatio);
        }

        /// <summary>
        /// 使用导航网格移动
        /// </summary>
        /// <param name="endPos"></param>
        public bool NavigationMoveStep(Vector3 tryEndPos, out Vector3 realEndPos)
        {
            var curPos = _entity.unityPosOfData;
            bool result = navigationCtrl.TryMove(curPos, tryEndPos, out realEndPos);
            if (!result)
            {
                return false;
            }

            Vector3 displayPos = realEndPos;
            //射线检测来修正脚底高度，SimpleMove边缘的时候会跳下去，SimpleMove只能传速度，无法控制落点。需要控制主角在导航网格内的落点
            if (GameScenePlayUtil.TryGroundRaycast(realEndPos, characterController.height, out var hit))
            {
                displayPos.y = hit.point.y;
            }

            Vector3 move = displayPos - transform.position;

            characterController.Move(move);

            return true;
        }

        #endregion 直接控制移动，而不是相应服务器同步

        #region 根据同步位置移动

        public virtual void SetSmoothMoveTarget(Vector3 target, float speedRatio, EntityMoveDirType dirType)
        {
            if (transform == null)
            {
                return;
            }

            this.transform.DOKill();
            this._rotationYAfterMove = null;

            this.targetPositon = target;

            float defaultSpeed = _entity.defaultMeterSpeed;
            //0.5代表step是角色走0.5秒的距离，这里设置成0.5代表客户端如果落后服务器0.5秒以上应该是有问题的
            float step = defaultSpeed * speedRatio * 0.5f;
            float sqrStep = step * step;

            var distance = (targetPositon - transform.position).sqrMagnitude;
            //单帧位移太大就瞬移，可看做保底，没什么问题
            if (distance > sqrStep * ViewEntityConst.QuickenStepThreshold)
            {
                StopPositionUpdate();
                SetPosition(targetPositon);
                return;
            }

            if (distance > sqrStep)
            {
                speedRatio *= 1.3f;
            }

            autoMoveUnnormalizedDir = target - transform.position;
            this.serverSyncSpeedRatio = speedRatio;
            this.dirType = dirType;
        }

        protected void StopPositionUpdate()
        {
            autoMoveUnnormalizedDir = Vector3.zero;
        }

        /// <summary>
        /// 刷新自动移动（服务器同步）
        /// </summary>
        /// <param name="deltaTime"></param>
        protected void AutoMoveUpdate(float deltaTime)
        {
            if (autoMoveUnnormalizedDir == Vector3.zero)
            {
                return;
            }

            float defaultSpeed = _entity.defaultMeterSpeed;
            float step = defaultSpeed * this.serverSyncSpeedRatio * deltaTime;
            //当前拟停下的位置
            Vector3 tryStopPos = Vector3.MoveTowards(transform.position, targetPositon, step);
            //当前目标点->最终目标点的矢量
            Vector3 curStopPosToEndPos = targetPositon - tryStopPos;
            //如果上述矢量和前进方向基本一致，则保持前进，否则（说明已经到达或越过终点）停下
            if (Vector3.Dot(curStopPosToEndPos, autoMoveUnnormalizedDir) <= 0)
            {
                StopPositionUpdate();
                SetPosition(targetPositon);
                SetAnimTargetSpeed(0);
                if (_rotationYAfterMove != null)
                {
                    //刷新旋转
                    RotateWithAnimation(_rotationYAfterMove.Value);
                    _rotationYAfterMove = null;
                }

                return;
            }

            switch (this.dirType)
            {
                case EntityMoveDirType.KeepVertical:
                    transform.LookAt(new Vector3(targetPositon.x, transform.position.y, targetPositon.z));
                    break;
                case EntityMoveDirType.Free:
                    transform.LookAt(new Vector3(targetPositon.x, targetPositon.y, targetPositon.z));
                    break;
                default:
                    break;
            }

            SetPosition(tryStopPos, true);
            bool notMoveHorizontally = Mathf.Abs(autoMoveUnnormalizedDir.x) < 0.001f &&
                                       Mathf.Abs(autoMoveUnnormalizedDir.z) < 0.001f;
            SetAnimTargetSpeed(notMoveHorizontally ? 0 : serverSyncSpeedRatio);
        }

        #endregion 根据同步位置移动

        public void StopAndSetPosition(Vector3 position)
        {
            StopMove(true);
            SetPosition(position);
        }

        #region 旋转

        public void StartSaveRotation()
        {
            rotatonNeedRestore = true;
            quaterion = this.transform.rotation;
        }

        public void EndSaveRotation()
        {
            rotatonNeedRestore = false;
        }


        /// <summary>
        /// 在移动完成后旋转
        /// 如果没有移动则立即旋转
        /// </summary>
        /// <param name="rotationY"></param>
        public void RotateAfterMove(int rotationY)
        {
            if (transform == null)
            {
                return;
            }

            if (autoMoveUnnormalizedDir != Vector3.zero)
            {
                // 当前正在移动，不刷新旋转。移动完毕之后刷新
                _rotationYAfterMove = rotationY;
                return;
            }

            RotateWithAnimation(rotationY);
        }

        /// <summary>
        /// 旋转并播放动画
        /// </summary>
        /// <param name="rotationY">欧拉角Y轴目标角度</param>
        /// <param name="duration">旋转用时，默认读取配置</param>
        /// <param name="delayTime">旋转前的延迟</param>
        /// <returns></returns>
        public UniTask RotateWithAnimation(float rotationY, float duration = -1, float delayTime = 0)
        {
            if (transform == null)
            {
                return UniTask.CompletedTask;
            }

            // 旋转朝向目标，并且只旋转y轴，使用rotateSpeed的速度去旋转
            var eulerAngle = this.transform.eulerAngles;
            eulerAngle.y = rotationY;
            // 使用DOTween旋转根据速度旋转过去
            return RotateWithAnimation(Quaternion.Euler(eulerAngle), duration, delayTime);
        }

        /// <summary>
        /// 旋转并播放动画
        /// </summary>
        /// <param name="lookAtPosition">旋转完成时实体面向的坐标</param>
        /// <param name="duration">旋转用时，默认读取配置</param>
        /// <param name="delayTime">旋转前的延迟</param>
        /// <returns></returns>
        public UniTask RotateWithAnimation(Vector3 lookAtPosition, float duration = -1, float delayTime = 0)
        {
            if (transform == null)
            {
                return UniTask.CompletedTask;
            }

            // 旋转朝向目标，并且只旋转y轴，使用rotateSpeed的速度去旋转
            var targetDir = lookAtPosition - this.transform.position;
            targetDir.y = 0;
            var targetRotation = Quaternion.LookRotation(targetDir, Vector3.up);
            return RotateWithAnimation(targetRotation, duration, delayTime);
        }

        /// <summary>
        /// 旋转并播放动画
        /// </summary>
        /// <param name="quaternion">目标旋转值</param>
        /// <param name="duration">旋转用时，默认读取配置</param>
        /// <param name="delayTime">旋转前的延迟</param>
        /// <returns></returns>
        public UniTask RotateWithAnimation(Quaternion quaternion, float duration = -1, float delayTime = 0)
        {
            if (transform == null)
            {
                return UniTask.CompletedTask;
            }

            if (duration == -1)
            {
                duration = _entity.cfgEntity.RotationTime / 1000f;
            }

            var currentRotation = this.transform.rotation;
            var angle = Quaternion.Angle(currentRotation, quaternion);
            if (angle < ViewEntityConst.AnimRotationThreshold)
            {
                return UniTask.CompletedTask;
            }

            this.transform.DOKill();
            // 计算旋转方向
            Vector3 v1 = currentRotation * Vector3.forward;
            Vector3 v2 = quaternion * Vector3.forward;
            bool isTurnLeft = Vector3.Cross(v1, v2).y < 0;
            if (delayTime == 0)
            {
                PlayTurnAnim(isTurnLeft);
                return this.transform.DORotateQuaternion(quaternion, duration).ToUniTask();
            }
            else
            {
                return DOTween.Sequence()
                    .SetLink(this.transform.gameObject)
                    .SetTarget(this.transform)
                    .AppendInterval(delayTime)
                    .AppendCallback(() => { PlayTurnAnim(isTurnLeft); })
                    .Append(this.transform.DORotateQuaternion(quaternion, duration)).ToUniTask();
            }
        }

        private void PlayTurnAnim(bool isTurnLeft)
        {
            if (animComponent == null)
            {
                return;
            }

            if (isTurnLeft)
            {
                animComponent.PlayTurnLeft();
            }
            else
            {
                animComponent.PlayTurnRight();
            }
        }

        public async UniTask StartLookAtTarget(Transform targetTrans)
        {
            transform.DOKill();
            await RotateWithAnimation(targetTrans.position);
            _keepLookAtTrans = targetTrans;
        }

        public void StopLookAtEntity()
        {
            if (_keepLookAtTrans == null)
            {
                return;
            }

            transform.DOKill();
            transform.LookAt(_keepLookAtTrans);
            _keepLookAtTrans = null;
        }

        #endregion 旋转

        public override void OnReset()
        {
            base.OnReset();
            OnHide();
            _entity = null;
            _animationTargetSpeed = 0;
            targetPositon = default;
            autoMoveUnnormalizedDir = default;
            dirType = default;
            serverSyncSpeedRatio = 0;
            _animationSpeed = 0;
            _rotationYAfterMove = null;
            _keepLookAtTrans = null;
            _animVelocity = 0;
            characterController = null;
            isPlayerAvatarEntity = false;
            quaterion = default;
            rotatonNeedRestore = false;
        }
    }
}